#include <glf/windows.h>
#include <intrin.h>
#pragma intrinsic(_ReadWriteBarrier)

namespace glf {

////////////////////////////////////////////////////////////////////////////////////////////////////
// Atomic 8
//
// There is no 8 bits atomic functions on windows; currently, 8 bits is acheived through the use
// of mutex. This will need to be refactored if you need to optimized on windows.
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class AtomicImpl8 {
public:
	inline AtomicImpl8();
	inline AtomicImpl8(const T& value);

	inline T fetchAndAdd(const T& value);
	inline T fetchAndSub(const T& value);
	inline T fetchAndOr(const T& value);
	inline T fetchAndAnd(const T& value);
	inline T fetchAndXor(const T& value);
	inline T fetchAndNand(const T& value);

	inline T addAndFetch(const T& value);
	inline T subAndFetch(const T& value);
	inline T orAndFetch(const T& value);
	inline T andAndFetch(const T& value);
	inline T xorAndFetch(const T& value);
	inline T nandAndFetch(const T& value);

	inline T compareAndSwap(const T& value, const T& comparand);

	inline T operator++();
	inline T operator--();
	inline T operator++(int);
	inline T operator--(int);
	inline T operator=(const T& );
	inline operator T() const;

protected:
	T mValue;
};

#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
extern Mutex AtomicLock;
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////
// Atomic 32
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class AtomicImpl32 {
public:
	inline AtomicImpl32();
	inline AtomicImpl32(const T& value);

	inline T fetchAndAdd(const T& value);
	inline T fetchAndSub(const T& value);
	inline T fetchAndOr(const T& value);
	inline T fetchAndAnd(const T& value);
	inline T fetchAndXor(const T& value);
	inline T fetchAndNand(const T& value);

	inline T addAndFetch(const T& value);
	inline T subAndFetch(const T& value);
	inline T orAndFetch(const T& value);
	inline T andAndFetch(const T& value);
	inline T xorAndFetch(const T& value);
	inline T nandAndFetch(const T& value);

	inline T compareAndSwap(const T& value, const T& comparand);

	inline T operator++();
	inline T operator--();
	inline T operator++(int);
	inline T operator--(int);
	inline T operator=(const T& );
	inline operator T() const;

protected:
	T mValue;
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Atomic 8
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
AtomicImpl8<T>::AtomicImpl8() {
}

template<typename T>
AtomicImpl8<T>::AtomicImpl8(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue = value;
}

template<typename T>
T AtomicImpl8<T>::fetchAndAdd(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	T oldValue = mValue;
	mValue += value;
	return oldValue;
}

template<typename T>
T AtomicImpl8<T>::fetchAndSub(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	T oldValue = mValue;
	mValue -= value;
	return oldValue;
}

template<typename T>
T AtomicImpl8<T>::fetchAndOr(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	T oldValue = mValue;
	mValue |= value;
	return oldValue;
}

template<typename T>
T AtomicImpl8<T>::fetchAndAnd(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	T oldValue = mValue;
	mValue &= value;
	return oldValue;
}

template<typename T>
T AtomicImpl8<T>::fetchAndXor(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	T oldValue = mValue;
	mValue ^= value;
	return oldValue;
}

template<typename T>
T AtomicImpl8<T>::fetchAndNand(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	T oldValue = mValue;
	mValue &= ~value;
	return oldValue;
}

template<typename T>
T AtomicImpl8<T>::addAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue += value;
	return mValue;
}

template<typename T>
T AtomicImpl8<T>::subAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue -= value;
	return mValue;
}

template<typename T>
T AtomicImpl8<T>::orAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue |= value;
	return mValue;
}

template<typename T>
T AtomicImpl8<T>::andAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue &= value;
	return mValue;
}

template<typename T>
T AtomicImpl8<T>::xorAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue ^= value;
	return mValue;
}

template<typename T>
T AtomicImpl8<T>::nandAndFetch(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue &= ~value;
	return mValue;
}

template<typename T>
T AtomicImpl8<T>::compareAndSwap(const T& value, const T& comparand) {
	return AtomicCompareAndSwap(mValue, value, comparand);
}

template<typename T>
T AtomicImpl8<T>::operator=(const T& value) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	mValue = value;
	return mValue;
}

template<typename T>
T AtomicImpl8<T>::operator ++() {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	return ++mValue;
}

template<typename T>
T AtomicImpl8<T>::operator --() {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	return --mValue;
}

template<typename T>
T AtomicImpl8<T>::operator ++(int) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	return mValue++;
}

template<typename T>
T AtomicImpl8<T>::operator --(int) {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif


	return mValue--;
}

template<typename T>
AtomicImpl8<T>::operator T() const {
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif

	return mValue;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Atomic 32
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
AtomicImpl32<T>::AtomicImpl32() {
	// the wrapper around interlocked exchange will only work if the sizezof(T) == sizeof(long)
	GLF_STATIC_ASSERT(sizeof(T) == sizeof(long));
}


template<typename T>
AtomicImpl32<T>::AtomicImpl32(const T& value) {
	InterlockedExchange((long*)&mValue, (long)value);
}

template<typename T>
T AtomicImpl32<T>::fetchAndAdd(const T& value) {
	return (T)InterlockedExchangeAdd((long*)&mValue, (long)value);
}

template<typename T>
T AtomicImpl32<T>::addAndFetch(const T& value) {
	T fetch = fetchAndAdd(value);
	return fetch + value;
}

template<typename T>
T AtomicImpl32<T>::fetchAndSub(const T& value) {
	return (T)InterlockedExchangeAdd((long*)&mValue, -(long)value);
}

template<typename T>
T AtomicImpl32<T>::subAndFetch(const T& value) {
	T fetch = fetchAndSub(value);
	return fetch - value;
}

template<typename T>
T AtomicImpl32<T>::compareAndSwap(const T& value, const T& comparand) {
	return AtomicCompareAndSwap(mValue, value, comparand);
}

template<typename T>
T AtomicImpl32<T>::operator ++() {
	T resulting = (T)InterlockedIncrement((long*)&mValue);
	return resulting;
}

template<typename T>
T AtomicImpl32<T>::operator ++(int) {
	T resulting = (T)InterlockedIncrement((long*)&mValue);
	return resulting - 1;
}

template<typename T>
T AtomicImpl32<T>::operator --() {
	T resulting = (T)InterlockedDecrement((long*)&mValue);
	return resulting;
}

template<typename T>
T AtomicImpl32<T>::operator --(int) {
	T resulting = (T)InterlockedDecrement((long*)&mValue);
	return resulting + 1;
}

template<typename T>
T AtomicImpl32<T>::operator=(const T& value) {
	return (T)InterlockedExchange((long*)&mValue, (long)value);
}

template<typename T>
AtomicImpl32<T>::operator T() const {
	return LoadWithAcquire(mValue);
}

#define GLF_ATOMIC_IMPL(AtomicType, type) \
	template<> class Atomic<type>: public AtomicType<type> { \
		public: \
			Atomic() {} \
			Atomic(const type& value) : AtomicType<type>(value) {} \
	}

GLF_ATOMIC_IMPL(AtomicImpl8,  bool);
GLF_ATOMIC_IMPL(AtomicImpl8,  char);
GLF_ATOMIC_IMPL(AtomicImpl8,  unsigned char);
GLF_ATOMIC_IMPL(AtomicImpl32, int);
GLF_ATOMIC_IMPL(AtomicImpl32, unsigned int);
GLF_ATOMIC_IMPL(AtomicImpl32, long);
GLF_ATOMIC_IMPL(AtomicImpl32, unsigned long);

#undef GLF_ATOMIC_IMPL

typedef Atomic<int> Atomic32;

template<typename T>
inline T AtomicCompareAndSwap32(volatile T& destination, T exchange, T comparand)
{
	return (T)InterlockedCompareExchange((long*)&destination, (long)exchange, (long)comparand);
}

template<typename T>
inline T AtomicIncrement32(volatile T& destination)
{
	return (T)InterlockedIncrement((long*)&destination);
}

template<typename T>
inline T AtomicDecrement32(volatile T& destination)
{
	return (T)InterlockedDecrement((long*)&destination);
}

template<typename T>
inline T AtomicCompareAndSwap(volatile T& destination, T exchange, T comparand)
{
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif
	
	T value = destination;
	if (value == comparand) destination = exchange;
	return value;
}

template<typename T>
inline T AtomicIncrement(volatile T& destination)
{
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif
	
	return ++destination;
}

template<typename T>
inline T AtomicDecrement(volatile T& destination)
{
#if !defined(GLF_DISABLE_DEFAULT_ATOMIC_WITH_MUTEX)
	LockScope lock(AtomicLock);
#endif
	
	return --destination;
}

#define GLF_ATOMIC_FUNC_IMPL(bits, type) \
	template<> inline type AtomicCompareAndSwap(type volatile& destination, type exchange, type comparand) { return AtomicCompareAndSwap##bits(destination, exchange, comparand); } \
	template<> inline type AtomicIncrement(type volatile& destination) { return AtomicIncrement##bits(destination); } \
	template<> inline type AtomicDecrement(type volatile& destination) { return AtomicDecrement##bits(destination); }

GLF_ATOMIC_FUNC_IMPL(32,  int);
GLF_ATOMIC_FUNC_IMPL(32,  unsigned int);
GLF_ATOMIC_FUNC_IMPL(32,  long);
GLF_ATOMIC_FUNC_IMPL(32,  unsigned long);

#ifndef _WIN64
GLF_ATOMIC_FUNC_IMPL(32,  void*);
#endif

#undef GLF_ATOMIC_FUNC_IMPL

inline void Membar()
{
	_ReadWriteBarrier();
}

} // namespace glf
